
from datetime import datetime
import zmq
from zmq.utils import jsonapi
import Cookie

import django.core
from django.http import HttpRequest, HttpResponse, QueryDict
from django.core.handlers import base


class ZMQRequest(HttpRequest):
    """docstring for ZMQRequest"""

    def __init__(self, environ, orig_req):
        super(ZMQRequest, self).__init__()

        self.params = {}
        self.path = ''
        self.path_info = ''
        self.method = None

        self.orig_request_str = orig_req

        zpo = jsonapi.loads(orig_req)
        self.orig_request_obj = zpo

        self.host = environ['ZMQ_HOST']
        self.port = environ['ZMQ_PORT']
        self.transport = environ['ZMQ_TRANSPORT']

        environ['SERVER_PORT'] = self.port

        # Enforce structure, method selection and URI.
        # Make optional ???
        if 'method' not in zpo:
            raise InvalidZMQObject("No 'method' key in dmq request object")

        if zpo['method'] not in ('GET', 'POST', 'PUT', 'DELETE'):
            raise InvalidZMQObject(
                        "Invalid method, got %s, I only except one of: %s" %
                        (zpo['method'], ('GET', 'POST', 'PUT', 'DELETE')))

        if 'uri' not in zpo:
            raise InvalidZMQObject("No 'uri' key in dmq request object")

        if 'uri' in zpo:
            environ['SERVER_NAME'] = zpo['domain']
        else:
            environ['SERVER_NAME'] = self.host

        script_name = base.get_script_name(environ)

        self.environ = environ
        self.path = zpo['uri']
        self.path_info = self.path
        if not self.path_info:
            self.path_info = u'/'

        self.META = environ
        self.META['PATH_INFO'] = self.path_info
        self.META['SCRIPT_NAME'] = script_name
        #self.method = environ['REQUEST_METHOD'].upper()
        self.method = zpo['method'].upper()

        self.zpo = zpo

        self.META = dict(environ.items())

        if 'params' zpo:
            qdict = QueryDict('', mutable=True, encoding=None)
            qdict.update(zpo['params'])
            self.params = qdict
            if self.method == 'GET' or self.method == 'DELETE':
                self.GET = self.params
            elif self.method == 'POST' or self.method == 'PUT':
                self.POST = self.params

    #__init__()

    def get_host(self):
        """Returns the ZMQ host using the environment or request parameters."""

        # We try two options, in order of decreasing preference.
        if 'ZMQ_HOST' in self.META:
            host = self.META['ZMQ_HOST']
            if 'ZMQ_PORT' in self.META:
                host = "%s:%s" % (host, self.META['ZMQ_PORT'])
                return host

        # Reconstruct the host using the algorithm from PEP 333.
        host = self.META['SERVER_NAME']
        server_port = str(self.META['SERVER_PORT'])
        if server_port != (self.is_secure() and '443' or '80'):
            host = '%s:%s' % (host, server_port)

        return host
    #get_host()

    def is_secure(self):
        """
        Always returns FAlse.

        As of writing this, there is no secure DMQ
        transport. We're taking security for granted.
        So Don't use django-dmq in an insecure environment!!!
        """
        return False
    #is_secure()

    def is_ajax(self):
        """
        Always returns FAlse.

        This is 0MQ, no Ajax for you.
        """
        return False
    #is_ajax()

    def _initialize_handlers(self):
        """
        Not sure how to handle this with ZMQ, do nothing
        for now.
        """
        pass
    #self._upload_handlers = [uploadhandler.load_handler(handler, self)
        #                         for handler in settings.FILE_UPLOAD_HANDLERS]
    #_initialize_handlers()

    def _set_upload_handlers(self, upload_handlers):
        """
        Not sure how to handle this with ZMQ, do nothing
        for now.
        """
        raise AttributeError(("You cannot set the upload handlers when using "
            "django-dmq. If you need this, please contact the "
            "django-dmq author with a description of what your doing and "
            "he'll help you make it work.")
    #_set_upload_handlers()

    def _get_upload_handlers(self):
        """
        Not sure how to handle this with ZMQ, do nothing
        for now.
        """
        return None
    #_get_upload_handlers()

    upload_handlers = property(_get_upload_handlers, _set_upload_handlers)

#ZMQRequest


class ZMQResponse(HttpResponse):
    """docstring for ZMQResponse"""

    def __init__(self, request, resp_dict, status=200):

        self.msg = {
            'success':True,
             'status':status,
             'payload':resp_dict,
        }

        if request.META.get('ZMQ_DEBUG', False):
            self.msg['orig_request_str'] = request.orig_request_str
            self.msg['orig_request_obj'] = request.orig_request_obj
            self.msg['time_stamp'] = datetime.now()


        super(ZMQResponse, self).__init__(
                content=self.msg,
                mimetype='text/plain',
                status=status,
                content_type='application/json')
    #__init__()

    def get_response(self, request):
        "Returns an ZMQResponse object for the given ZMQRequest"
        from django.core import exceptions, urlresolvers
        from django.conf import settings

        try:
            # Setup default url resolver for this thread, this code is outside
            # the try/except so we don't get a spurious "unbound local
            # variable" exception in the event an exception is raised before
            # resolver is set
            urlconf = settings.ROOT_URLCONF
            urlresolvers.set_urlconf(urlconf)
            resolver = urlresolvers.RegexURLResolver(r'^/', urlconf)
            try:
                response = None
                # Apply request middleware
                for middleware_method in self._request_middleware:
                    response = middleware_method(request)
                    if response:
                        break

                if response is None:
                    if hasattr(request, "urlconf"):
                        # Reset url resolver with a custom urlconf.
                        urlconf = request.urlconf
                        urlresolvers.set_urlconf(urlconf)
                        resolver = urlresolvers.RegexURLResolver(r'^/',
                                                                urlconf)

                    callback, callback_args, callback_kwargs = (
                        resolver.resolve(request.path_info))

                    # Apply view middleware
                    for middleware_method in self._view_middleware:
                        response = middleware_method(request, callback,
                                                    callback_args,
                                                    callback_kwargs)
                        if response:
                            break

                if response is None:
                    try:
                        response = callback(request,
                                            *callback_args,
                                            **callback_kwargs)
                    except Exception, e:
                        # If the view raised an exception,
                        # run it through exception
                        # middleware, and if the exception middleware returns a
                        # response, use that. Otherwise, reraise the exception.
                        for middleware_method in self._exception_middleware:
                            response = middleware_method(request, e)
                            if response:
                                break
                        if response is None:
                            raise

                # Complain if the view returned None (a common error).
                if response is None:
                    try:
                        view_name = callback.func_name # If it's a function
                    except AttributeError:
                        # If it's a class
                        view_name = callback.__class__.__name__ + '.__call__'
                    raise ValueError(("The view %s.%s didn't return an "
                                    "ZMQResponse object.") %
                                    (callback.__module__, view_name))

                # If the response supports deferred rendering, apply template
                # response middleware and the render the response
                if hasattr(response, 'render') and callable(response.render):
                    for middleware_method in (
                            self._template_response_middleware):
                        response = middleware_method(request, response)
                    response.render()

            except http.Http404, e:
                logger.warning('Not Found: %s' % request.path,
                   extra={
                           'status_code': 404,
                           'request': request
                           })
                if settings.DEBUG:
                    from django.views import debug
                    response = debug.technical_404_response(request, e)
                else:
                    try:
                        callback, param_dict = resolver.resolve404()
                        response = callback(request, **param_dict)
                    except:
                        try:
                            response = self.handle_uncaught_exception(
                                                            request,
                                                            resolver,
                                                            sys.exc_info())
                        finally:
                            receivers = (
                                signals.got_request_exception.send(
                                                sender=self.__class__,
                                                request=request))
            except exceptions.PermissionDenied:
                logger.warning('Forbidden (Permission denied): %s' %
                                            request.path,
                                            extra={
                                                    'status_code': 403,
                                                    'request': request
                                                    })
                response = ZMQResponseForbidden('Permission denied')
            except SystemExit:
                #Allow sys.exit() to actually exit. See tickets #1023 and #4701
                raise
            except:
                # Handle everything else,
                # including SuspiciousOperation, etc.
                # Get the exception info now,
                # in case another exception is thrown later.
                receivers = signals.got_request_exception.send(
                                                        sender=self.__class__,
                                                        request=request)
                response = self.handle_uncaught_exception(
                                                        request,
                                                        resolver,
                                                        sys.exc_info())
        finally:
            # Reset URLconf for this thread on the way out for complete
            # isolation of request.urlconf
            urlresolvers.set_urlconf(None)

        try:
            # Apply response middleware, regardless of the response
            for middleware_method in self._response_middleware:
                response = middleware_method(request, response)
            response = self.apply_response_fixes(request, response)
        except: # Any exception should be gathered and handled
            receivers = signals.got_request_exception.send(
                                                    sender=self.__class__,
                                                    request=request)
            response = self.handle_uncaught_exception(
                                                    request,
                                                    resolver,
                                                    sys.exc_info())

        return response
    #get_response()

    def __str__(self):
        """JSON encoding of response"""
        return jsonapi.dumps(self.msg,
                            cls=django.core.serializers.json.DjangoJSONEncoder)
    #__str__()
    __repr__ = __str__

#ZMQResponse

class ZMQResponseNotModified(ZMQResponse):
    status_code = 304

class ZMQResponseBadRequest(ZMQResponse):
    status_code = 400

class ZMQResponseNotFound(ZMQResponse):
    status_code = 404

class ZMQResponseForbidden(ZMQResponse):
    status_code = 403

class ZMQResponseNotAllowed(ZMQResponse):
    status_code = 405

    def __init__(self, permitted_methods):
        super(ZMQResponseNotAllowed, self).__init__()
        self['Allow'] = ', '.join(permitted_methods)

class ZMQResponseGone(ZMQResponse):
    status_code = 410

class ZMQResponseServerError(ZMQResponse):
    status_code = 500
